#include <dspSimulator/instruction/extu.h>


bool EXTU::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void EXTU::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                             std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
}

std::shared_ptr<Instruction> EXTU::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<EXTU> extu;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<6>(data, 2, 0b000010) && existStandardCondition(data)) {
            extu = std::make_shared<EXTU>(dspInstInfo->getAddress(), baseCycle);
            extu->creg_z = extractBits<4>(data, 28);
            extu->dst = extractBits<5>(data, 23).to_ulong();
            extu->src2 = extractBits<5>(data, 18).to_ulong();
            extu->cst = extractBits<5>(data, 13).to_ulong();
            extu->auxCst = extractBits<5>(data, 8).to_ulong();
            extu->s = data[1];
            extu->p = data[0];
            if (extu->s == 0) {
                extu->funcUnit = FuncUnit::S1;
            } else {
                extu->funcUnit = FuncUnit::S2;
            }
            extu->instFormat = InstFormat::Kind1;
        } else if (checkBits<10>(data, 2, 0b1010111000) && existStandardCondition(data)) {
            extu = std::make_shared<EXTU>(dspInstInfo->getAddress(), baseCycle);
            extu->creg_z = extractBits<4>(data, 28);
            extu->dst = extractBits<5>(data, 23).to_ulong();
            extu->src2 = extractBits<5>(data, 18).to_ulong();
            extu->src1 = extractBits<5>(data, 13).to_ulong();
            extu->x = data[12];
            extu->s = data[1];
            extu->p = data[0];
            if (extu->s == 0) {
                extu->funcUnit = FuncUnit::S1;
            } else {
                extu->funcUnit = FuncUnit::S2;
            }
            extu->instFormat = InstFormat::Kind2;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (data[10] == 0 && checkBits<2>(data, 5, 0b00) && checkBits<4>(data, 1, 0b0001)) {
            extu = std::make_shared<EXTU>(dspInstInfo->getAddress(), baseCycle);
            extu->isCompactInstruction = true;
            extu->compactInstKind = "Sc5";
            extu->cst = extractBits<3>(data, 13).to_ulong();
            extu->cst += (extractBits<2>(data, 11).to_ulong()) << 3;
            extu->auxCst = 31;
            extu->src2 = extractBits<3>(data, 7).to_ulong();
            extu->dst = 0;
            extu->s = data[0];
            if (extu->s == 0) {
                extu->funcUnit = FuncUnit::S1;
            } else {
                extu->funcUnit = FuncUnit::S2;
            }
            extu->DSZ = dspInstInfo->getDSZ();
            extu->RS = dspInstInfo->getRS();
            extu->SAT = dspInstInfo->getSAT();
            extu->PROT = dspInstInfo->getPROT();
            extu->BR = dspInstInfo->getBR();
            extu->p = dspInstInfo->getP();
            extu->fixUpRegOnCompactInstruction();
            extu->instFormat = InstFormat::Kind1;
        }
    }
    return extu;
}

void EXTU::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    u32 csta = 0;
    u32 cstb = 0;
    u32 data = *src2LoRegPtr;
    if (instFormat == InstFormat::Kind1) {
        csta = cst;
        cstb = auxCst;
    } else if (instFormat == InstFormat::Kind2) {
        std::bitset<32> src1 = *src1LoRegPtr;
        for (u32 i = 0; i <= 4; i++) {
            csta += static_cast<u32>(src1[i]) << i;
        }
        for (u32 i = 5; i <= 9; i++) {
            cstb += static_cast<u32>(src1[i]) << (i - 5);
        }
    }
    data <<= csta;
    data >>= cstb;
    *dstLoRegPtr = data;
}

std::string EXTU::toString() const {
    std::string res;
    if (!isCompactInstruction) {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " ";
    if (instFormat == InstFormat::Kind1) {
        res += getSrc2Reg32Name() + "," + std::to_string(cst) + "," + std::to_string(auxCst) +
               "," + getDstReg32Name();
    } else if (instFormat == InstFormat::Kind2) {
        res += getSrc2Reg32Name() + "," + getSrc1Reg32Name() +
               "," + getDstReg32Name();
    }
    return res;
}

std::shared_ptr<DspInstruction> EXTU::clone() {
    return std::make_shared<EXTU>(*this);
}
